package websocket

import (
	"flag"
	"fmt"
	"github.com/gorilla/websocket"
	"net/http"
	"strings"
	"study/testproject/source/bean"
	"study/testproject/source/service"
	"study/testproject/source/util"
	"study/testproject/source/util/httpSession"
	"text/template"
)

//存放要发送的数据、当前发送用户 sendSessionId，接收者用户 receiveSessionId
type msgsession struct {
	cast    []byte
	sendUid int
}
type hub struct {
	// 注册了的连接器
	connections map[int]*connection

	// 从连接器中注册请求
	register chan *connection

	// 从连接器中注销请求
	unregister chan *connection

	// 从连接器中发入的信息
	msgcast chan *msgsession
}

//初始化hub
var h = hub{
	register:    make(chan *connection),
	unregister:  make(chan *connection),
	connections: make(map[int]*connection),
	msgcast:     make(chan *msgsession),
}

//监听connection注册、注销、发送信息
func (h *hub) run() {
	for {
		select {
		case c := <-h.register:
			h.connections[c.user.Uid] = c
		//fmt.Println("注册session=", c.sessionId, " 注册user=", c.user)
		case c := <-h.unregister:
			fmt.Println("注销user=", c.user)
			if _, ok := h.connections[c.user.Uid]; ok {
				//删除connection ,delete(集合，key)
				delete(h.connections, c.user.Uid)
				//关闭send发送信息通道
				close(c.send)
			}
		case m := <-h.msgcast:
			//获取发送者连接
			sc := h.connections[m.sendUid]
			sendUser := sc.user
			//发送内容转换为string->json
			msg := string(m.cast)
			dataJson := util.StrToJson(msg)
			fmt.Println(msg, dataJson)
			uids := dataJson.Uids
			var sendMsg []byte //:= []byte(dataJson.Data)
			time := util.GetTime()
			//遍历获取要接收人的连接然后发送信息
			//此处为遍历接收人信息，页面中可能会选择多个接收人来发送
			for _, uid := range uids {
				if uid == m.sendUid {
					fmt.Println("不能向自己发送消息")
				} else {
					//接收人的连接
					hc := h.connections[uid]
					//接收人不在线
					if hc == nil {
						//可将不在线的联系人接收信息保存到数据表中
						//上线后读取 不在线.保存到数据表中状态 未读=0
						fmt.Println("接收人", uid, "不在线.保存到数据表中状态 未读=0")
						service.SendMessage(m.sendUid, uid, 0, time, dataJson.Data)
					} else {
						//接收人在线
						//获取接收人
						user := hc.user
						fmt.Println("接收人=", uid, " 发送人=", m.sendUid, " user=", user)
						//不在线.保存到数据表中状态 已读=1
						service.SendMessage(m.sendUid, uid, 1, time, dataJson.Data)
						//生成信息内容
						sendStr := `{"msgtype":0,"uid":"` + fmt.Sprint(sendUser.Uid) + `","nick":"` + sendUser.Unick + `","portrait":"` + sendUser.Uportrait + `","time":"` + time + `","content":"` + dataJson.Data + `"}`
						sendMsg = []byte(sendStr)
						//发送信息
						hc.send <- sendMsg
					}
				}
			}
			if sc != nil {
				//msgtype用于判断是发送给自己的还是发送给别人的
				//生成信息内容 发送给自己用于页面展示 ，也可以在页面自动生成
				sendStr := `{"msgtype":1,"uid":"` + fmt.Sprint(sendUser.Uid) + `","nick":"` + sendUser.Unick + `","portrait":"` + sendUser.Uportrait + `","time":"` + time + `","content":"` + dataJson.Data + `"}`
				sendMsg = []byte(sendStr)
				//发送信息
				sc.send <- sendMsg
			}
		}
	}
}

//连接信息的结构
type connection struct {
	// websocket 连接器
	ws *websocket.Conn

	// 发送信息的缓冲 channel
	send      chan []byte
	sessionId string
	user      bean.Users
}

//读取信息
func (c *connection) reader(uid int) {
	for {
		_, message, err := c.ws.ReadMessage()
		if err != nil {
			break
		}
		msn := msgsession{cast: message, sendUid: uid}
		//将消息msn写入msgcast通道
		h.msgcast <- &msn
	}
	c.ws.Close()
}

//输出信息
func (c *connection) writer() {
	//监控send通道是否有可读信息
	for message := range c.send {
		//fmt.Println("writer=", string(message))
		err := c.ws.WriteMessage(websocket.TextMessage, message)
		//发送信息结束
		if err != nil {
			break
		}
	}
	c.ws.Close()
}

var upgrader = &websocket.Upgrader{ReadBufferSize: 1024, WriteBufferSize: 1024}

//websocket创建连接和接、发数据
//每次调用该控制都会在当前会话中创建一个新的当前会话的内容
func WsHandler(w http.ResponseWriter, r *http.Request) {
	ws, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		return
	}
	//获取session
	session := httpSession.GetSession(w, r)
	//获取session值
	sessionval := session.Get(util.USER_SESSION_KEY)
	//将session值转换为user
	user, _ := sessionval.(bean.Users)

	//获取当前用户连接
	//判断是否注册过连接
	hc := h.connections[user.Uid]
	fmt.Println("hc=", hc, "sessionId=", session.SessionID())

	if hc != nil && !strings.EqualFold(hc.sessionId, session.SessionID()) {
		fmt.Println("已经在别处登录")
		err := hc.ws.WriteMessage(websocket.TextMessage, []byte(`{"msgtype":"2","content":"系统提示:您已经在别处登录"}`))
		//发送信息结束
		if err != nil {
			fmt.Println(err)
		}
	}
	//创建新connection
	c := &connection{send: make(chan []byte, 256), ws: ws, sessionId: session.SessionID(), user: user}
	//将connection传入register通道
	h.register <- c
	defer func() {
		//将connection传入unregister通道
		h.unregister <- c
	}()
	//开启一个goroutine,当前会话连接通过send通道获取接收信息
	go c.writer()
	//读取接收信息
	c.reader(user.Uid)
}

//启动WebSocket相关服务
func Start() {
	//解析命令
	flag.Parse()
	//监听服务
	go h.run()
	http.HandleFunc("/se", ToWebSocketHtml)
	http.HandleFunc("/ws", WsHandler)
	http.HandleFunc("/unsession", UnSession)
}
func UnSession(w http.ResponseWriter, r *http.Request) {
	//获取session
	session := httpSession.GetSession(w, r)
	session.Delete(util.USER_SESSION_KEY)
	w.Write([]byte("会话已被注销"))
}

//跳转到页面
func ToWebSocketHtml(rw http.ResponseWriter, r *http.Request) {
	session := httpSession.GetSession(rw, r)
	user := session.Get(util.USER_SESSION_KEY)
	if user == nil {
		tp, err := template.ParseFiles(util.HTML_PATH_ROOT + "login.html")
		if err != nil {
			fmt.Println(err)
			return
		}
		tp.Execute(rw, nil)
	} else {
		tp, err := template.ParseFiles("static/html/websocket.html")
		if err != nil {
			fmt.Println(err)
			return
		}
		tp.Execute(rw, nil)
	}
}
